use anyhow::{Context, anyhow};
use clap::Parser;
use cli::Action;
use polars::{
    io::SerWriter,
    prelude::{CsvWriter, JoinArgs, JoinType, LazyFrame, col},
};
use std::{fs::File, io::Write, process::exit};

mod cli;
mod errors;
mod file;
mod manual;
mod parser;

fn main() -> anyhow::Result<()> {
    let args = cli::Args::parse();

    if args.verbose {
        unsafe {
            std::env::set_var("RUST_LOG", "debug");
        }
    } else {
        if std::env::var("RUST_LOG").is_err() {
            unsafe {
                std::env::set_var("RUST_LOG", "info");
            }
        }
    }
    env_logger::init();

    log::debug!("Arguments:\n{:#?}", args);

    if let Some(command) = args.command {
        match command {
            Action::Manual { manual_list } => {
                write!(std::io::stdout(), "{}", manual_list.display_manual()).unwrap();
            }
        }

        exit(0);
    }

    // Parse cli parameters
    let mut res_lf: Option<LazyFrame> = None;
    for input_complex_str in &args.input_complex {
        let file_spec = file::FileSpec::from_str(input_complex_str)
            .with_context(|| format!("Failed to parse file spec: {}", input_complex_str))?;

        log::debug!("Parsing file: {}", file_spec.file_path);
        let lf = file_spec
            .lazy_read_to_lazyframe()
            .with_context(|| format!("Failed to read file: {}", file_spec.file_path))?;

        if let Some(ref mut res_lf) = res_lf {
            *res_lf = res_lf.clone().join(
                lf,
                [col("FID"), col("IID")],
                [col("FID"), col("IID")],
                JoinArgs::new(JoinType::Inner),
            );
        } else {
            res_lf = Some(lf);
        }
    }

    let mut res_df = res_lf.clone().unwrap().collect()?;

    log::debug!("Final result: {:?}", res_df);

    let writer: Box<dyn Write>;

    match args.output_file {
        Some(output_file) => {
            writer = Box::new(File::create(output_file)?);
        }
        None => {
            writer = Box::new(std::io::stdout());
        }
    }

    let mut csv_writer = CsvWriter::new(writer)
        .with_separator(b'\t')
        .include_header(true);

    log::debug!("Writing result to CSV...");

    csv_writer
        .finish(&mut res_df)
        .with_context(|| anyhow!("Failed to write result."))?;

    log::debug!("Done.");
    Ok(())
}
